home *** CD-ROM | disk | FTP | other *** search
/ Programming Languages Suite / ProgramD2.iso / Borland / Borland C++ V5.02 / OWLSRC.PAK / LAYOUTWI.CPP < prev    next >
Encoding:
C/C++ Source or Header  |  1997-05-06  |  31.8 KB  |  1,232 lines

  1. //----------------------------------------------------------------------------
  2. // ObjectWindows
  3. // Copyright (c) 1992, 1997 by Borland International, All Rights Reserved
  4. //
  5. //$Revision:   10.8  $
  6. //
  7. // Implementation of class TLayoutWindow.
  8. //----------------------------------------------------------------------------
  9. #include <owl/pch.h>
  10. #if !defined(OWL_LAYOUTWI_H)
  11. # include <owl/layoutwi.h>
  12. #endif
  13. #if !defined(WINSYS_UIMETRIC_H)
  14. # include <winsys/uimetric.h>
  15. #endif
  16. #if !defined(CLASSLIB_FIXEDPNT_H)
  17. # include <classlib/fixedpnt.h>
  18. #endif
  19.  
  20. OWL_DIAGINFO;
  21. //----------------------------------------------------------------------------
  22.  
  23. #if defined(BI_NAMESPACE)
  24. namespace OWL {
  25. #endif
  26.  
  27. // Small wrapper around the 'DeferWindowPos' APIs
  28. //
  29. class TDeferWinPos {
  30.   public:
  31.     TDeferWinPos(int numWindows);
  32.    ~TDeferWinPos();
  33.     bool  DeferWindowPos(HWND hwnd, HWND after, int x, int y, int cx, int cy,
  34.                          uint flags);
  35.     bool  EndDeferWindowPos();
  36.   protected:
  37.     HDWP  Hdwp;
  38. };
  39.  
  40. #if defined(BI_NAMESPACE)
  41. } // namespace OWL
  42. #endif
  43.  
  44. //
  45. //
  46. //
  47. TDeferWinPos::TDeferWinPos(int numWindows)
  48. {
  49.   Hdwp = numWindows ? ::BeginDeferWindowPos(numWindows) : 0;
  50. }
  51.  
  52. //
  53. //
  54. //
  55. bool
  56. TDeferWinPos::DeferWindowPos(HWND hwnd, HWND after, int x, int y, 
  57.                              int cx, int cy, uint flags)
  58. {
  59.   PRECONDITION(Hdwp);
  60.   Hdwp = ::DeferWindowPos(Hdwp, hwnd, after, x, y, cx, cy, flags);
  61.   CHECK(Hdwp);
  62.   return Hdwp != 0;
  63. }
  64.  
  65. //
  66. //
  67. //
  68. bool  
  69. TDeferWinPos::EndDeferWindowPos()
  70. {
  71.   PRECONDITION(Hdwp);
  72.   if (::EndDeferWindowPos(Hdwp)) {
  73.     Hdwp = 0;
  74.     return true;
  75.   }
  76.   return false;
  77. }
  78.  
  79. //
  80. //
  81. //
  82. TDeferWinPos::~TDeferWinPos()
  83. {
  84.   if (Hdwp)
  85.     EndDeferWindowPos();
  86. }
  87.  
  88.  
  89. DEFINE_RESPONSE_TABLE1(TLayoutWindow, TWindow)
  90.   EV_WM_SIZE,
  91. END_RESPONSE_TABLE;
  92.  
  93. IMPLEMENT_CASTABLE(TLayoutWindow);
  94.  
  95. //
  96. // Constraints can have up to three input variables
  97. //
  98. // the method for solving the constraint is represented as an ordered linear
  99. // combination of the inputs and the constant with the constant expressed last
  100. //
  101.  
  102. #if defined(BI_NAMESPACE)
  103. namespace OWL {
  104. #endif
  105.  
  106. struct TVariable;
  107. struct TConstraint {
  108.   TVariable*    Inputs[3];
  109.   TVariable*    Output;
  110.   TFixedPoint   OrderedCombination[4];
  111.   TConstraint*  Next;
  112.  
  113.   TConstraint();
  114.  
  115.   bool  IsResolved();  // iff its inputs have been resolved
  116.   int   Evaluate();
  117.   int   NumActualInputs();
  118. };
  119.  
  120. struct TVariable {
  121.   int           Value;
  122.   TConstraint*  DeterminedBy;  // 0 if variable is constant
  123.   bool          Resolved;
  124.  
  125.   TVariable() {Value = 0; DeterminedBy = 0;}
  126. };
  127.  
  128. //
  129. // The layout metrics represent four equations. For equations that are
  130. // "absolute" or "as is" we don't add a constraint and just set the variable
  131. // value directly(and mark the variable as constant); otherwise we produce an
  132. // ordered linear combination from the equation and add a constraint
  133. //
  134. struct TChildMetrics {
  135.   public:
  136.     bool            GeneratedConstraints;
  137.     TWindow*        Child;
  138.     TLayoutMetrics  Metrics;
  139.     TVariable       Variables[4];  // x => 0, y => 1, right => 2, bottom => 3
  140.     TChildMetrics*  Next;
  141.  
  142.     TChildMetrics(TWindow& child, TLayoutMetrics& metrics);
  143. };
  144.  
  145. #if defined(BI_NAMESPACE)
  146. } // namespace OWL
  147. #endif
  148.  
  149. //----------------------------------------------------------------------------
  150.  
  151. //
  152. //
  153. //
  154. TLayoutWindow::TLayoutWindow(TWindow*        parent,
  155.                              const char far* title,
  156.                              TModule*        module)
  157. :
  158.   TWindow(parent, title, module)
  159. {
  160.   // Initialize virtual bases, in case the derived-most used default ctor
  161.   //
  162.   TWindow::Init(parent, title, module);
  163.  
  164.   NumChildMetrics = 0;
  165.   ChildMetrics = 0;
  166.   Constraints = 0;
  167.   Plan = 0;
  168.   PlanIsDirty = false;
  169.   ClientSize.cx = ClientSize.cy = 0;
  170.  
  171.   // Allocate variables for the parent's left, top, right, and bottom and
  172.   // mark them as resolved
  173.   //
  174.   Variables = new TVariable[4];
  175.   Variables[0].Resolved = true;
  176.   Variables[1].Resolved = true;
  177.   Variables[2].Resolved = true;
  178.   Variables[3].Resolved = true;
  179. }
  180.  
  181. //
  182. //
  183. //
  184. TLayoutWindow::~TLayoutWindow()
  185. {
  186.   delete[] Variables;
  187.  
  188.   // Free the child metrics
  189.   //
  190.   for (TChildMetrics* childMetrics = ChildMetrics; childMetrics;) {
  191.     TChildMetrics*  tmp = childMetrics;
  192.     childMetrics = childMetrics->Next;
  193.     delete tmp;
  194.   }
  195.  
  196.   // Free the constraints
  197.   //
  198.   ClearPlan();
  199.   for (TConstraint* c = Constraints; c;) {
  200.     TConstraint*  tmp = c;
  201.     c = c->Next;
  202.     delete tmp;
  203.   }
  204. }
  205.  
  206. static bool hasBorder(TWindow* win)
  207. {
  208.   // We consider it to have a border unless it is a pop-up or child window
  209.   // without WS_BORDER set
  210.   //
  211.   if ((win->Attr.Style & (WS_CHILD|WS_POPUP)) && !(win->Attr.Style & WS_BORDER))
  212.     return false;
  213.  
  214.   else
  215.     return true;
  216. }
  217.  
  218. //
  219. //
  220. //
  221. void
  222. TLayoutWindow::Layout()
  223. {
  224.   if (ChildMetrics) {
  225.     TChildMetrics*  childMetrics;
  226.  
  227.     GetFontHeight();
  228.  
  229.     // Initialize the parent's variables
  230.     //
  231.     Variables[2].Value = ClientSize.cx - 1;
  232.     Variables[3].Value = ClientSize.cy - 1;
  233.  
  234.     if (hasBorder(this)) {
  235.       int  cxBorder = TUIMetric::CxBorder;
  236.       int  cyBorder = TUIMetric::CyBorder;
  237.  
  238.       Variables[0].Value = -cxBorder;
  239.       Variables[1].Value = -cyBorder;
  240.       Variables[2].Value += cxBorder;
  241.       Variables[3].Value += cyBorder;
  242.     }
  243.     else {
  244.       Variables[0].Value = 0;
  245.       Variables[1].Value = 0;
  246.     }
  247.  
  248.     // Rebuild layout plan if necessary
  249.     //
  250.     if (PlanIsDirty) {
  251.       PlanIsDirty = false;
  252.  
  253.       for (childMetrics = ChildMetrics; childMetrics;
  254.            childMetrics = childMetrics->Next)
  255.         BuildConstraints(*childMetrics);
  256.  
  257.       BuildPlan();
  258.     }
  259.  
  260.     // Use the plan to calculate actual child window position values
  261.     //
  262.     ExecutePlan();
  263.  
  264.     // Find out how many windows we're dealing with
  265.     //
  266.     int numWindows = 0;
  267.     for (childMetrics = ChildMetrics; childMetrics; childMetrics = childMetrics->Next) {
  268.       TWindow*    win = childMetrics->Child;
  269.       if (win->GetHandle())
  270.         numWindows++;
  271.     }
  272.  
  273. #if !defined(OWL_NO_DEFERWINDOWPOS_LAYOUT)
  274.     // Helper object to use 'DefWindowPos' API
  275.     //
  276.     TDeferWinPos dwp(numWindows);
  277. #endif
  278.  
  279.     // Do actual resizing
  280.     //
  281.     for (childMetrics = ChildMetrics; childMetrics; childMetrics = childMetrics->Next) {
  282.       TWindow*    win = childMetrics->Child;
  283.       TVariable*  variables = childMetrics->Variables;
  284.  
  285.       if (win->GetHandle()) {
  286.  
  287. #if defined(OWL_NO_DEFERWINDOWPOS_LAYOUT)
  288.         win->SetWindowPos(
  289.           0,
  290.           variables[0].Value,
  291.           variables[1].Value,
  292.           variables[2].Value - variables[0].Value + 1,
  293.           variables[3].Value - variables[1].Value + 1,
  294.           SWP_NOACTIVATE | SWP_NOZORDER
  295.         );
  296. #else
  297.         dwp.DeferWindowPos(*win, 0, 
  298.                            variables[0].Value,
  299.                            variables[1].Value,
  300.                            variables[2].Value - variables[0].Value + 1,
  301.                            variables[3].Value - variables[1].Value + 1,
  302.                            SWP_NOACTIVATE | SWP_NOZORDER);
  303. #endif
  304.       }
  305.       else {
  306.         win->Attr.X = variables[0].Value;
  307.         win->Attr.Y = variables[1].Value;
  308.         win->Attr.W = variables[2].Value - variables[0].Value + 1;
  309.         win->Attr.H = variables[3].Value - variables[1].Value + 1;
  310.       }
  311.     }
  312.   }
  313. }
  314. //
  315. //
  316. //
  317. void
  318. TLayoutWindow::SetChildLayoutMetrics(TWindow& child, TLayoutMetrics& metrics)
  319. {
  320.   PRECONDITION(&child);
  321.   PRECONDITION(&metrics);
  322.  
  323.   PlanIsDirty = true;
  324.  
  325.   if (ChildMetrics) {
  326.  
  327.     // See if we already have metrics for the child
  328.     //
  329.     for (TChildMetrics* childMetrics = ChildMetrics; childMetrics;
  330.          childMetrics = childMetrics->Next) {
  331.       if (childMetrics->Child == &child) {
  332.         childMetrics->Child = &child;
  333.         childMetrics->Metrics = metrics;
  334.  
  335.         // Get rid of the old constraints
  336.         //
  337.         RemoveConstraints(*childMetrics);
  338.         return;
  339.       }
  340.     }
  341.   }
  342.  
  343.   TChildMetrics* childMetrics = new TChildMetrics(child, metrics);
  344.   childMetrics->Next = ChildMetrics;
  345.   ChildMetrics = childMetrics;
  346.   NumChildMetrics++;
  347. }
  348.  
  349. //
  350. //
  351. //
  352. bool
  353. TLayoutWindow::GetChildLayoutMetrics(TWindow& child, TLayoutMetrics& metrics)
  354. {
  355.   PRECONDITION(&child);
  356.  
  357.   TChildMetrics* childMetrics = GetChildMetrics(child);
  358.   if (childMetrics) {
  359.     metrics = childMetrics->Metrics;
  360.     return true;
  361.   }
  362.   return false;
  363. }
  364.  
  365. //
  366. // Remove child (layout) metrics for a given child (if found) and update
  367. // other children as necessary
  368. //
  369. bool
  370. TLayoutWindow::RemoveChildLayoutMetrics(TWindow& child)
  371. {
  372.   PRECONDITION(&child);
  373.  
  374.   for (TChildMetrics** childMetrics = &ChildMetrics; *childMetrics;
  375.        childMetrics = &(*childMetrics)->Next) {
  376.     if ((*childMetrics)->Child == &child) {
  377.  
  378.       // Unlink target metrics from list & clean up a bit
  379.       //
  380.       TChildMetrics* tmp = *childMetrics;
  381.       *childMetrics = tmp->Next;
  382.       RemoveConstraints(*tmp);
  383.       NumChildMetrics--;
  384.  
  385.       // Update other child metrics now that removed metric is gone
  386.       // Check for case where new relWin is lmParent and adjust other edge
  387.       // to be what removed window was using. If an 'edge' is really a size,
  388.       // then give up & just leave it asis. If the removed window had an edge
  389.       // that was really a size, then use the other constraint in that
  390.       // dimension (X or Y)
  391.       //
  392.       for (TChildMetrics* cm = ChildMetrics; cm; cm = cm->Next) {
  393.         if (cm->Metrics.X.RelWin == &child) {
  394.           RemoveConstraints(*cm);
  395.           cm->Metrics.X.RelWin = tmp->Metrics.X.RelWin;
  396.           if (cm->Metrics.X.RelWin == lmParent)
  397.             cm->Metrics.X.OtherEdge = tmp->Metrics.X.OtherEdge;
  398.         }
  399.         if (cm->Metrics.Y.RelWin == &child) {
  400.           RemoveConstraints(*cm);
  401.           cm->Metrics.Y.RelWin = tmp->Metrics.Y.RelWin;
  402.           if (cm->Metrics.Y.RelWin == lmParent)
  403.             cm->Metrics.Y.OtherEdge = tmp->Metrics.Y.OtherEdge;
  404.         }
  405.         if (cm->Metrics.Width.RelWin == &child) {
  406.           RemoveConstraints(*cm);
  407.           if (cm->Metrics.Width.MyEdge == lmWidth)
  408.             cm->Metrics.Width.Relationship = lmAsIs;
  409.           else {
  410.             if (tmp->Metrics.Width.MyEdge == lmWidth) {
  411.               cm->Metrics.Width.RelWin = tmp->Metrics.X.RelWin;
  412.               if (cm->Metrics.Width.RelWin == lmParent)
  413.                 cm->Metrics.Width.OtherEdge = tmp->Metrics.X.OtherEdge;
  414.             }
  415.             else {
  416.               cm->Metrics.Width.RelWin = tmp->Metrics.Width.RelWin;
  417.               if (cm->Metrics.Width.RelWin == lmParent)
  418.                 cm->Metrics.Width.OtherEdge = tmp->Metrics.Width.OtherEdge;
  419.             }
  420.           }
  421.         }
  422.         if (cm->Metrics.Height.RelWin == &child) {
  423.           RemoveConstraints(*cm);
  424.           if (cm->Metrics.Height.MyEdge == lmHeight)
  425.             cm->Metrics.Height.Relationship = lmAsIs;
  426.           else {
  427.             if (tmp->Metrics.Height.MyEdge == lmHeight) {
  428.               cm->Metrics.Height.RelWin = tmp->Metrics.Y.RelWin;
  429.               if (cm->Metrics.Height.RelWin == lmParent)
  430.                 cm->Metrics.Height.OtherEdge = tmp->Metrics.Y.OtherEdge;
  431.             }
  432.             else {
  433.               cm->Metrics.Height.RelWin = tmp->Metrics.Height.RelWin;
  434.               if (cm->Metrics.Height.RelWin == lmParent)
  435.                 cm->Metrics.Height.OtherEdge = tmp->Metrics.Height.OtherEdge;
  436.             }
  437.           }
  438.         }
  439.       }
  440.  
  441.       // Finaly, delete target metrics
  442.       //
  443.       delete tmp;
  444.       return true;
  445.     }
  446.   }
  447.   return false;
  448. }
  449.  
  450. //----------------------------------------------------------------------------
  451.  
  452. //
  453. //
  454. //
  455. void
  456. TLayoutWindow::EvSize(uint sizeType, TSize& size)
  457. {
  458.   TWindow::EvSize(sizeType, size);
  459.  
  460.   if (sizeType != SIZE_MINIMIZED && size != ClientSize) {
  461.     ClientSize = size;
  462.     Layout();
  463.   }
  464. }
  465.  
  466. //
  467. //
  468. //
  469. void
  470. TLayoutWindow::RemoveChild(TWindow* child)
  471. {
  472.   TWindow::RemoveChild(child);
  473.   RemoveChildLayoutMetrics(*child);
  474. }
  475.  
  476. //----------------------------------------------------------------------------
  477.  
  478. //
  479. //
  480. //
  481. TChildMetrics*
  482. TLayoutWindow::GetChildMetrics(TWindow& child)
  483. {
  484.   PRECONDITION(&child);
  485.  
  486.   for (TChildMetrics* childMetrics = ChildMetrics; childMetrics;
  487.        childMetrics = childMetrics->Next)
  488.     if (childMetrics->Child == &child)
  489.       return childMetrics;
  490.  
  491.   return 0;
  492. }
  493.  
  494. //
  495. //
  496. //
  497. void
  498. TLayoutWindow::ExecutePlan()
  499. {
  500.   for (TConstraint* c = Plan; c; c = c->Next)
  501.     c->Output->Value = c->Evaluate();
  502. }
  503.  
  504. //
  505. //
  506. //
  507. void
  508. TLayoutWindow::ClearPlan()
  509. {
  510.   if (Plan) {
  511.     // Move all constraints that were in the plan back to the list of
  512.     // constraints
  513.     //
  514.     if (!Constraints)
  515.       Constraints = Plan;
  516.  
  517.     else {
  518.       TConstraint* c;
  519.       for (c = Constraints; c->Next; c = c->Next)
  520.         ;
  521.  
  522.       c->Next = Plan;
  523.     }
  524.  
  525.     Plan = 0;
  526.   }
  527. }
  528.  
  529. //
  530. //
  531. //
  532. void
  533. TLayoutWindow::BuildPlan()
  534. {
  535.   TChildMetrics*  childMetrics;
  536.   TConstraint*    lastInPlan = 0;
  537.  
  538.   ClearPlan();
  539.  
  540.   // Mark all variables that aren't determined by a constraint as resolved
  541.   //
  542.   for (childMetrics = ChildMetrics; childMetrics; childMetrics = childMetrics->Next) {
  543.     TVariable*  variable = childMetrics->Variables;
  544.  
  545.     variable->Resolved = variable->DeterminedBy ? false : true;
  546.     variable++;
  547.     variable->Resolved = variable->DeterminedBy ? false : true;
  548.     variable++;
  549.     variable->Resolved = variable->DeterminedBy ? false : true;
  550.     variable++;
  551.     variable->Resolved = variable->DeterminedBy ? false : true;
  552.   }
  553.  
  554.   // Uses local propagation as much as possible (because it's fast)
  555.   //
  556.   // If cycles exist then we will end up with constraints that haven't been
  557.   // added to the plan. we convert the remaining constraints into simultaneous
  558.   // linear equations which we solve using Gaussian elimination
  559.   //
  560.   // Look for constraints that have all their input variables resolved and
  561.   // append them to the plan
  562.   //
  563.   for (bool foundOne = true; foundOne;) {
  564.     TConstraint*  c = Constraints;
  565.     TConstraint*  previous = 0;
  566.  
  567.     foundOne = false;
  568.  
  569.     while (c) {
  570.       if (c->IsResolved()) {
  571.         TConstraint*  resolved = c;
  572.  
  573.         c->Output->Resolved = true;
  574.         foundOne = true;
  575.  
  576.         // Extract the constraint from the list of constraints
  577.         //
  578.         if (previous)
  579.           previous->Next = c->Next;
  580.  
  581.         else
  582.           Constraints = c->Next;
  583.  
  584.         c = c->Next;
  585.  
  586.         // Append the constraint to the plan
  587.         //
  588.         if (lastInPlan)
  589.           lastInPlan->Next = resolved;
  590.  
  591.         else
  592.           Plan = resolved;
  593.  
  594.         lastInPlan = resolved;
  595.       }
  596.       else {
  597.         previous = c;
  598.         c = c->Next;
  599.       }
  600.     }
  601.   }
  602.  
  603.   // Gaussian elimination not currently supported--give up
  604.   //
  605.   if (Constraints)
  606.     TXWindow::Raise(this, IDS_LAYOUTINCOMPLETE);
  607. }
  608.  
  609. //
  610. //
  611. //
  612. static int
  613. findInput(TConstraint* simplify, TVariable* input)
  614. {
  615.   for (int i = 0; i < 3; i++)
  616.     if (simplify->Inputs[i] == input)
  617.       return i;
  618.  
  619.   return -1;
  620. }
  621.  
  622. //
  623. // simplify constraint "simplify" by substituting constraint "_using"
  624. //
  625. // we do this when the two constraints are defined in terms of each other
  626. //   1. the output of "simplify" is an input of "_using"
  627. //   2. the output of "_using" is an input of "simplify"
  628. //
  629. // we do this to avoid a layout cycle
  630. //
  631. // "output" is the output variable for constraint "_using"
  632. //
  633. static
  634. void
  635. Simplify(TConstraint* simplify, TVariable* output, TConstraint* _using)
  636. {
  637.   if (!simplify)
  638.     return;
  639.  
  640.   int  outputOfSimplify = findInput(_using, simplify->Output);  // check #1
  641.   int  target = findInput(simplify, output);  // check #2
  642.  
  643.   if (outputOfSimplify != -1 && target != -1) {
  644.     int  commonInputs[3];
  645.     int  numInputsOfUsing = _using->NumActualInputs();
  646.     int  i;
  647.  
  648.     // Count how many inputs are common between "simplify" and "_using"
  649.     //
  650.     for (i = 0; i < numInputsOfUsing; i++)
  651.       commonInputs[i] = findInput(simplify, _using->Inputs[i]);
  652.  
  653.     // Since constraints only have room for 3 inputs we can not simplify if the
  654.     // total number of the existing inputs minus the input we are going to back
  655.     // substitute for plus the number of inputs added by "_using" (i.e. inputs
  656.     // not common between the two constraints) exceeds 3
  657.     //
  658.     int  numInputsOfSimplify = simplify->NumActualInputs() - 1;
  659.     int  newInputs = 0;
  660.  
  661.     // Compute the number of additional inputs contributed by "_using"
  662.     //
  663.     for (i = 0; i < numInputsOfUsing; i++)
  664.       if (commonInputs[i] == -1 && i != outputOfSimplify)
  665.         newInputs++;
  666.  
  667.     if (numInputsOfSimplify + newInputs > 3)
  668.       return;
  669.  
  670.     TFixedPoint  m = simplify->OrderedCombination[target];
  671.  
  672.     // Adjust the constant part
  673.     //
  674.     simplify->OrderedCombination[3] += m * _using->OrderedCombination[3];
  675.  
  676.     // Merge the common inputs
  677.     //
  678.     for (i = 0; i < numInputsOfUsing; i++)
  679.       if (commonInputs[i] != -1)
  680.         simplify->OrderedCombination[commonInputs[i]] +=
  681.           m * _using->OrderedCombination[i];
  682.  
  683.     simplify->Inputs[target] = 0;  // input has been back substituted out
  684.  
  685.     // If necessary shift the inputs following "output" (and their associated
  686.     // mutiplier) left by one...
  687.     //
  688.     for (i = target + 1; i < 3; i++)
  689.       if (simplify->Inputs[i]) {
  690.         simplify->Inputs[i - 1] = simplify->Inputs[i];
  691.         simplify->Inputs[i] = 0;
  692.         simplify->OrderedCombination[i - 1] = simplify->OrderedCombination[i];
  693.       }
  694.  
  695.     // Add the new inputs
  696.     //
  697.     for (i = 0; i < numInputsOfUsing; i++)
  698.       if (commonInputs[i] == -1 && i != outputOfSimplify) {
  699.         simplify->Inputs[numInputsOfSimplify] = _using->Inputs[i];
  700.         simplify->OrderedCombination[numInputsOfSimplify] =
  701.           m * _using->OrderedCombination[i];
  702.         numInputsOfSimplify++;
  703.       }
  704.  
  705.     // Now scale things back so that the output of "simplify" is 1
  706.     //
  707.     TFixedPoint  f = 1 - m;
  708.  
  709.     simplify->OrderedCombination[3] /= f;
  710.     for (i = 0; i < numInputsOfSimplify; i++)
  711.       simplify->OrderedCombination[i] /= f;
  712.   }
  713. }
  714.  
  715. //
  716. //
  717. //
  718. void
  719. TLayoutWindow::AddConstraint(TChildMetrics&     metrics,
  720.                              TLayoutConstraint* c,
  721.                              TWhichConstraint   whichConstraint)
  722. {
  723.   int           index;
  724.   TVariable*    output;
  725.   TConstraint*  result = new TConstraint;
  726.  
  727.   // Set the output variable
  728.   //
  729.   if (whichConstraint == XConstraint && metrics.Metrics.X.MyEdge == lmRight)
  730.     output = &metrics.Variables[2];
  731.  
  732.   else if (whichConstraint == YConstraint && metrics.Metrics.Y.MyEdge == lmBottom)
  733.     output = &metrics.Variables[3];
  734.  
  735.   else
  736.     output = &metrics.Variables[whichConstraint];
  737.  
  738.   // Set the inputs based on the edge
  739.   //
  740.   if (c->Relationship != lmAbsolute && c->Relationship != lmAsIs) {
  741.     TVariable*  variables;
  742.  
  743.     if (c->RelWin == lmParent)
  744.       variables = Variables;
  745.  
  746.     else {
  747.       TChildMetrics*  relWinMetrics = GetChildMetrics(*c->RelWin);
  748.       if (!relWinMetrics) {
  749.         delete result;
  750.         TXWindow::Raise(this, IDS_LAYOUTBADRELWIN);
  751.       }
  752.       variables = relWinMetrics->Variables;
  753.     }
  754.  
  755.     switch (c->OtherEdge) {
  756.       case lmLeft:
  757.       case lmTop:
  758.       case lmRight:
  759.       case lmBottom:
  760.         result->Inputs[0] = &variables[c->OtherEdge];
  761.         break;
  762.  
  763.       case lmWidth:
  764.       case lmHeight:
  765.         // width => right - left + 1
  766.         // height => bottom - top + 1
  767.         //
  768.         result->Inputs[0] = &variables[c->OtherEdge - lmWidth+lmRight];
  769.         result->Inputs[1] = &variables[c->OtherEdge - lmWidth+lmLeft];
  770.         result->OrderedCombination[1] = -1;
  771.         result->OrderedCombination[3] = 1;
  772.         break;
  773.  
  774.       case lmCenter:
  775.         switch (whichConstraint) {
  776.           case XConstraint:
  777.           case WidthConstraint:
  778.             // Center => (left + right) / 2
  779.             //
  780.             result->Inputs[0] = &variables[0];
  781.             result->Inputs[1] = &variables[2];
  782.             break;
  783.  
  784.           case YConstraint:
  785.           case HeightConstraint:
  786.             // Center => (top + bottom) / 2
  787.             //
  788.             result->Inputs[0] = &variables[1];
  789.             result->Inputs[1] = &variables[3];
  790.             break;
  791.         }
  792.         result->OrderedCombination[0] = result->OrderedCombination[1] = TFixedPoint(1,2);
  793.         break;
  794.     }
  795.   }
  796.  
  797.   // Now store the constant term as the last of the ordered linear combination
  798.   //
  799.   // We must do this after setting the inputs
  800.   //
  801.   // NOTE: we cannot assume that the constant part is 0, because it might have
  802.   //       been set above
  803.   //
  804.   switch (c->Relationship) {
  805.     case lmAsIs:
  806.       result->OrderedCombination[3] += whichConstraint == WidthConstraint ?
  807.                                                           metrics.Child->Attr.W :
  808.                                                           metrics.Child->Attr.H;
  809.       break;
  810.  
  811.     case lmAbsolute:
  812.     case lmSameAs:
  813.     case lmBelow:
  814.     case lmAbove: {
  815.       int  value = c->Units == lmPixels ? c->Value : LayoutUnitsToPixels(c->Value);
  816.  
  817.       if (c->Relationship == lmAbove)
  818.         value = -value - 1;
  819.  
  820.       else if (c->Relationship == lmBelow)
  821.         value++;
  822.  
  823.       result->OrderedCombination[3] += value;
  824.       break;
  825.     }
  826.  
  827.     case lmPercentOf:
  828.       TFixedPoint  percent = c->Percent;
  829.  
  830.       percent /= 100;
  831.       result->OrderedCombination[0] *= percent;
  832.       result->OrderedCombination[3] *= percent;
  833.  
  834.       switch (c->OtherEdge) {
  835.         case lmWidth:
  836.         case lmHeight:
  837.         case lmCenter:
  838.           result->OrderedCombination[1] *= percent;
  839.           break;
  840.       }
  841.       break;
  842.   }
  843.  
  844.   // Now handle cases where the left hand side is width, height, or center
  845.   //
  846.   // This must be done last...
  847.   //
  848.   if (result->Inputs[0])
  849.     index = result->Inputs[1] ? 2 : 1;
  850.  
  851.   else
  852.     index = 0;
  853.  
  854.   switch (c->MyEdge) {
  855.     case lmWidth:
  856.       if (whichConstraint == XConstraint || metrics.Metrics.X.MyEdge == lmRight) {
  857.         // Rewrite "right - left + 1 = " as "left = right - (...) + 1"
  858.         //
  859.         for (int i = 0; i < index; i++)
  860.           result->OrderedCombination[i] = -result->OrderedCombination[i];
  861.  
  862.         result->OrderedCombination[3] = -result->OrderedCombination[3];
  863.         result->OrderedCombination[3]++;
  864.         result->Inputs[index] = &metrics.Variables[2];
  865.  
  866.         if (whichConstraint == WidthConstraint)
  867.           output = &metrics.Variables[XConstraint];
  868.       }
  869.       else {
  870.         // Rewrite "right - left + 1 = " as "right = left + ... - 1"
  871.         //
  872.         result->Inputs[index] = &metrics.Variables[0];
  873.         result->OrderedCombination[3]--;
  874.  
  875.         Simplify(metrics.Variables[0].DeterminedBy, output, result);
  876.       }
  877.       break;
  878.  
  879.     case lmHeight:
  880.       if (whichConstraint == YConstraint || metrics.Metrics.Y.MyEdge == lmBottom) {
  881.         // Rewrite "bottom - top + 1 = " as "top = bottom - (...) + 1"
  882.         //
  883.         for (int i = 0; i < index; i++)
  884.           result->OrderedCombination[i] = -result->OrderedCombination[i];
  885.  
  886.         result->OrderedCombination[3] = -result->OrderedCombination[3];
  887.         result->OrderedCombination[3]++;
  888.         result->Inputs[index] = &metrics.Variables[3];
  889.  
  890.         if (whichConstraint == HeightConstraint)
  891.           output = &metrics.Variables[YConstraint];
  892.       }
  893.       else {
  894.         // Rewrite "bottom - top + 1 = " as "bottom = top + ... - 1"
  895.         //
  896.         result->Inputs[index] = &metrics.Variables[1];
  897.         result->OrderedCombination[3]--;
  898.  
  899.         Simplify(metrics.Variables[1].DeterminedBy, output, result);
  900.       }
  901.       break;
  902.  
  903.     case lmCenter:
  904.       TVariable*  input = &metrics.Variables[0];  // left
  905.  
  906.       switch (whichConstraint) {
  907.         case XConstraint:
  908.           // Rewrite "(left + right) / 2 = " as "left = -right + 2 * (...)"
  909.           //
  910.           input += 2;  // right
  911.           break;
  912.  
  913.         case YConstraint:
  914.           // Rewrite "(top + bottom) / 2 = " as "top = -bottom + 2 * (...)"
  915.           //
  916.           input += 3;  // bottom
  917.           break;
  918.  
  919.         case WidthConstraint:
  920.           // Rewrite "(left + right) / 2 = " as "right = -left + 2 * (...)" or
  921.           // "left = -right + 2 * (...)" depending on whether the "x" constraint
  922.           // is left or right
  923.           //
  924.           if (metrics.Metrics.X.MyEdge == lmRight) {
  925.             input += 2;  // right
  926.             output = &metrics.Variables[XConstraint];
  927.           }
  928.           break;
  929.  
  930.         case HeightConstraint:
  931.           // Rewrite "(top + bottom) / 2 = " as "bottom = -top + 2 * (...)" or
  932.           // "top = -bottom + 2 * (...)" depending on whether the "y" constraint
  933.           // is top or bottom
  934.           //
  935.           if (metrics.Metrics.Y.MyEdge != lmBottom)
  936.             input++;  // top
  937.  
  938.           else {
  939.             input += 3;  // bottom
  940.             output = &metrics.Variables[XConstraint];
  941.           }
  942.           break;
  943.       }
  944.       result->Inputs[index] = input;
  945.       for (int i = 0; i < index; i++)
  946.         result->OrderedCombination[i] <<= 1;
  947.  
  948.       result->OrderedCombination[3] <<= 1;
  949.       result->OrderedCombination[index] = -1;
  950.       break;
  951.   }
  952.  
  953.   // Now set the constraint output
  954.   //
  955.   output->DeterminedBy = result;
  956.   result->Output = output;
  957.  
  958.   // Add the constraint to the list of constraints
  959.   //
  960.   result->Next = Constraints;
  961.   Constraints = result;
  962. }
  963.  
  964. //
  965. //
  966. //
  967. void
  968. TLayoutWindow::RemoveConstraints(TChildMetrics& childMetrics)
  969. {
  970.   TVariable*  variable = childMetrics.Variables;
  971.  
  972.   PlanIsDirty = true;
  973.   ClearPlan();
  974.   childMetrics.GeneratedConstraints = false;
  975.  
  976.   for (int i = 0; i < 4; i++) {
  977.     TConstraint*  constraint = variable->DeterminedBy;
  978.  
  979.     variable->Value = 0;
  980.  
  981.     if (constraint) {
  982.  
  983.       // Remove the constraint from the list of constraints
  984.       //
  985.       if (Constraints == constraint)
  986.         Constraints = constraint->Next;
  987.  
  988.       else
  989.         for (TConstraint*  c = Constraints; c->Next; c = c->Next)
  990.           if (c->Next == constraint) {
  991.             c->Next = constraint->Next;
  992.             break;
  993.           }
  994.  
  995.       delete constraint;
  996.       variable->DeterminedBy = 0;
  997.     }
  998.  
  999.     variable++;
  1000.   }
  1001. }
  1002.  
  1003. //
  1004. //
  1005. //
  1006. void
  1007. TLayoutWindow::BuildConstraints(TChildMetrics& childMetrics)
  1008. {
  1009.   // NOTE: to get uniformity we consider the window edges to sit on pixels
  1010.   //       and not between pixels. so our idea of right is left + width - 1
  1011.   //       and not left + width
  1012.   //
  1013.   if (!childMetrics.GeneratedConstraints) {
  1014.     TLayoutConstraint*  c = &childMetrics.Metrics.X;
  1015.  
  1016.     childMetrics.GeneratedConstraints = true;
  1017.  
  1018.     // "x" can be one of: left, right, center
  1019.     //
  1020.     if (c->Relationship == lmAsIs)
  1021.       if (c->MyEdge == lmLeft)
  1022.         childMetrics.Variables[0].Value = childMetrics.Child->Attr.X;
  1023.  
  1024.       else
  1025.         childMetrics.Variables[2].Value = childMetrics.Child->Attr.X +
  1026.                                           childMetrics.Child->Attr.W - 1;
  1027.  
  1028.     else if (c->Relationship == lmAbsolute && c->MyEdge != lmCenter) {
  1029.       int  value = c->Units == lmPixels ? c->Value : LayoutUnitsToPixels(c->Value);
  1030.  
  1031.       childMetrics.Variables[c->MyEdge == lmLeft ? 0 : 2].Value = value;
  1032.     }
  1033.     else {
  1034.       AddConstraint(childMetrics, c, XConstraint);
  1035.     }
  1036.  
  1037.     // "y" can be one of: top, bottom, center
  1038.     //
  1039.     c = &childMetrics.Metrics.Y;
  1040.  
  1041.     if (c->Relationship == lmAsIs)
  1042.       if (c->MyEdge == lmTop)
  1043.         childMetrics.Variables[1].Value = childMetrics.Child->Attr.Y;
  1044.  
  1045.       else
  1046.         childMetrics.Variables[3].Value = childMetrics.Child->Attr.Y +
  1047.                                           childMetrics.Child->Attr.H - 1;
  1048.  
  1049.     else if (c->Relationship == lmAbsolute && c->MyEdge != lmCenter) {
  1050.       int  value = c->Units == lmPixels ? c->Value : LayoutUnitsToPixels(c->Value);
  1051.  
  1052.       childMetrics.Variables[c->MyEdge == lmTop ? 1 : 3].Value = value;
  1053.     }
  1054.     else {
  1055.       AddConstraint(childMetrics, c, YConstraint);
  1056.     }
  1057.  
  1058.     // "width" can be one of: width, right, center
  1059.     //
  1060.     c = &childMetrics.Metrics.Width;
  1061.  
  1062.     if (c->MyEdge == lmRight && (c->Relationship == lmAsIs || c->Relationship == lmAbsolute))
  1063.       childMetrics.Variables[2].Value = c->Relationship == lmAsIs ?
  1064.                                         childMetrics.Child->Attr.X +
  1065.                                         childMetrics.Child->Attr.W - 1 :
  1066.                                         c->Units == lmPixels ? c->Value : LayoutUnitsToPixels(c->Value);
  1067.  
  1068.     else
  1069.       AddConstraint(childMetrics, c, WidthConstraint);
  1070.  
  1071.     // "height" can be one of: height, bottom, center
  1072.     //
  1073.     c = &childMetrics.Metrics.Height;
  1074.  
  1075.     if (c->MyEdge == lmBottom && (c->Relationship == lmAsIs || c->Relationship == lmAbsolute))
  1076.       childMetrics.Variables[3].Value = c->Relationship == lmAsIs ?
  1077.                                         childMetrics.Child->Attr.Y +
  1078.                                         childMetrics.Child->Attr.H - 1 :
  1079.                                         c->Units == lmPixels ? c->Value : LayoutUnitsToPixels(c->Value);
  1080.  
  1081.     else
  1082.       AddConstraint(childMetrics, c, HeightConstraint);
  1083.   }
  1084. }
  1085.  
  1086. //
  1087. //
  1088. //
  1089. int
  1090. TLayoutWindow::LayoutUnitsToPixels(int value)
  1091. {
  1092.   const long  UnitsPerEM = 8;
  1093.  
  1094.   return int((long(value) * FontHeight + UnitsPerEM / 2) / UnitsPerEM);
  1095. }
  1096.  
  1097. //
  1098. //
  1099. //
  1100. void
  1101. TLayoutWindow::GetFontHeight()
  1102. {
  1103.   HFONT hFont = 0;
  1104.   if (GetHandle())
  1105.     hFont = HFONT(HandleMessage(WM_GETFONT));
  1106.  
  1107.   // NOTE: It's fairly customary to return NULL to the WM_GETFONT
  1108.   //       request - specially when the window is using the system
  1109.   //       font - Hence, we'll default to the system font too...
  1110.   //
  1111.   if (!hFont)
  1112.     hFont = HFONT(GetStockObject(SYSTEM_FONT));
  1113.  
  1114.   CHECK(hFont);
  1115.   TFont font(hFont);
  1116.   FontHeight = font.GetHeight();
  1117. }
  1118.  
  1119. //----------------------------------------------------------------------------
  1120.  
  1121. //
  1122. //
  1123. //
  1124. TChildMetrics::TChildMetrics(TWindow&        child,
  1125.                              TLayoutMetrics& metrics)
  1126. :
  1127.   Child(&child),
  1128.   Metrics(metrics)
  1129. {
  1130.   GeneratedConstraints = false;
  1131.   Next = 0;
  1132. }
  1133.  
  1134. //
  1135. //
  1136. //
  1137. TConstraint::TConstraint()
  1138. {
  1139.   Inputs[0] = Inputs[1] = Inputs[2] = 0;
  1140.   OrderedCombination[0] = OrderedCombination[1] = OrderedCombination[2] = 1;
  1141.  
  1142.   // NOTE: OrderedCombination[3] was initialized to 0 by the TFixedPoint ctor
  1143.   //
  1144.   Output = 0;
  1145. }
  1146.  
  1147. //
  1148. //
  1149. //
  1150. bool
  1151. TConstraint::IsResolved()
  1152. {
  1153.   return (!Inputs[0] || Inputs[0]->Resolved) &&
  1154.          (!Inputs[1] || Inputs[1]->Resolved) &&
  1155.          (!Inputs[2] || Inputs[2]->Resolved);
  1156. }
  1157.  
  1158. //
  1159. //
  1160. //
  1161. int
  1162. TConstraint::Evaluate()
  1163. {
  1164.   TFixedPoint  value = OrderedCombination[3];  // initialize to constant part
  1165.  
  1166.   if (Inputs[0])
  1167.     value += OrderedCombination[0] * Inputs[0]->Value;
  1168.  
  1169.   if (Inputs[1])
  1170.     value += OrderedCombination[1] * Inputs[1]->Value;
  1171.  
  1172.   if (Inputs[2])
  1173.     value += OrderedCombination[2] * Inputs[2]->Value;
  1174.  
  1175.   return value;
  1176. }
  1177.  
  1178. //
  1179. //
  1180. //
  1181. int
  1182. TConstraint::NumActualInputs()
  1183. {
  1184.   int i;
  1185.   for (i = 0; i < 3; i++)
  1186.     if (!Inputs[i])
  1187.       break;
  1188.  
  1189.   return i;
  1190. }
  1191.  
  1192. //----------------------------------------------------------------------------
  1193.  
  1194. //
  1195. //
  1196. //
  1197. TLayoutMetrics::TLayoutMetrics()
  1198. {
  1199.   X.RelWin = 0;
  1200.   X.MyEdge = X.OtherEdge = lmLeft;
  1201.   X.Relationship = lmAsIs;
  1202.   X.Units = lmLayoutUnits;
  1203.   X.Value = 0;
  1204.  
  1205.   Y.RelWin = 0;
  1206.   Y.MyEdge = Y.OtherEdge = lmTop;
  1207.   Y.Relationship = lmAsIs;
  1208.   Y.Units = lmLayoutUnits;
  1209.   Y.Value = 0;
  1210.  
  1211.   Width.RelWin = 0;
  1212.   Width.MyEdge = Width.OtherEdge = lmWidth;
  1213.   Width.Relationship = lmAsIs;
  1214.   Width.Units = lmLayoutUnits;
  1215.   Width.Value = 0;
  1216.  
  1217.   Height.RelWin = 0;
  1218.   Height.MyEdge = Height.OtherEdge = lmHeight;
  1219.   Height.Relationship = lmAsIs;
  1220.   Height.Units = lmLayoutUnits;
  1221.   Height.Value = 0;
  1222. }
  1223.  
  1224. //
  1225. //
  1226. //
  1227. void
  1228. TLayoutMetrics::SetMeasurementUnits(TMeasurementUnits units)
  1229. {
  1230.   X.Units = Y.Units = Width.Units = Height.Units = units;
  1231. }
  1232.